
module(..., package.seeall)

require("cosmo")

get_nav_bar = function (page, params, sputnik)

   cur_page = sputnik:dirify(page.name)		 

   local nav_page, error = sputnik:load_config_page(
                 sputnik.config.DEFAULT_NAVIGATION_BAR)
   
   if error or (not nav_page.NAVIGATION) then
      return "Couldn't parse the navigation bar: " .. (error or "")
   end

   local nav = nav_page.NAVIGATION

   for i, section in ipairs(nav_page.NAVIGATION) do
      if not section.title then section.title = section.id end
      section.id = sputnik:dirify(section.id)
      if section.id == cur_page or section.id == page.category then
	 section.is_active = true
	 nav.current_section = section
      end

      for j, subsection in ipairs(section) do
	 if not subsection.title then subsection.title = subsection.id end
	 subsection.id = sputnik:dirify(subsection.id)
	 if subsection.id == cur_page or subsection.id == page.category then
	    section.is_active = true
	    nav.current_section = section
	    subsection.is_active = true
	 end
      end
   end

   return cosmo.fill(page.templates.NAV_BAR,
      {  do_sections = function() 
	    for i, section in ipairs(nav) do
	       local class = "back"
	       if section.is_active then 
		  class = "front"
	       end
	       cosmo.yield { 
		  class = class,
		  id    = section.id,
		  url   = sputnik:make_url(section.id),  
		  label = section.title}
	    end
	 end,
	 do_subsections = function() 
	    for i, subsection in ipairs(nav.current_section or {}) do
	       local class = "back"
	       if subsection.is_active then
		  class = "front"
	       end
	       cosmo.yield { 
		  class = class,
		  url = sputnik:make_url(subsection.id),
		  label = subsection.title 
	       }
	    end
	 end,
      }
   )
end

local get_history_table = function (page, sputnik)
   if not page.history then return {} end
   return {  
      version          = page.version,
      author           = page.author,
      if_only_version  = cosmo.cond(#page.history.all < 2, {}),
      if_last_version  = cosmo.cond((page.version == page.history.current) 
				    and page.history.previous,
				    { diff_url = page.urls:diff{
					  version = page.history.current,
					  other = page.history.previous
				       }
				    }
				 ),
      if_exists_later  = cosmo.cond(not(page.version == page.history.current),
				    { diff_url   = page.urls:diff{
					  version = page.history.current,
					  other = page.version
				       }
				    }
				 ),
      if_exist_earlier = cosmo.cond(page.history.previous,
				    { older_link = page.urls:show{
					  version = page.history.previous
				       }
				    }
				 ),
      history_url      = page.urls:history(),
   }
end

local HtmlDiff = {

   diffsplit = function (self, text) 
      local l = {}
      text = text .. " "
      for token, sep in string.gmatch(text, "([^%s]*)(%s)") do
	 l[#l+1] = token
	 l[#l+1] = sep
      end
      return l
   end,

   get_html_diff = function (self, t1, t2)

      local html = "<pre><code>"

      local t2 = self:diffsplit(t2)
      local t1 = self:diffsplit(t1)

      local l = list.new({})
      require("lcs")

      local subseq = lcs.longestCommonSubseq(t1, t2, l)

      is = 1
      i1 = 0
      i2 = 1

      local state = ""
     
      function indiff(text)
	 if #text > 0 then 
	    return "<ins class='diffmod'>" .. text .. "</ins>"
	 else
	    return ""
	 end
      end

      function outdiff(text) 
	 if #text > 0 then
	    return "<del class='diffmod'>" .. text .. "</del>"
	 else 
	    return ""
	 end
      end

      while true do

	 i1 = i1 + 1

	 if i1 > #t1 then                                  -- done with t1 
	    for j=i2,#t2 do
	       html = html .. indiff(t2[j])
	    end
	    for j=is,#subseq do
	       html = html .. subseq[j]
	    end
	    break                                          -- done
	 end

	 if t1[i1] == subseq[is] then 
	    while true do   ---- fastforward through T2 ---
	       if t2[i2] == t1[i1] or i2 > #t2 then 
		  break 
	       else
		  if t2[i2] then 
		     html = html .. indiff(t2[i2])	
		  end
		  i2 = i2 + 1
	       end
	    end

	    --html = html .. outdiff(t1[i1])
	    i2 = i2 + 1
	    if subseq[is] then 
	       html = html .. subseq[is]
	    end
	    is = is + 1
	 else 
	    html = html .. outdiff(t1[i1])
	 end
      end

      html = html .. "</code></pre>"
      return html
   end,
}


-- ---------------------------------------------------------------------
--     Actions
-- ---------------------------------------------------------------------

actions = {}

actions.raw_save = function(page, params, sputnik)
   if not params.user then
      params.try_again = NOT_LOGGED_IN
      return actions.raw_edit(page, params, sputnik)
   elseif params.submit == "cancel" then
      return actions.show(page, params, sputnik)
   else
      sputnik:save_page(page, params.raw_page)
      page = sputnik:load_config_page(params.raw_page, {})
      sputnik:decorate_page(page)
      return actions.show(page, params, sputnik)
   end
end

actions.save = function(page, params, sputnik)
   if not params.user then
      params.try_again = NOT_LOGGED_IN
      return actions.edit(page, params, sputnik)
   elseif params.submit == "cancel" then
      return actions.show(page, params, sputnik)
   else
      page.title    = params.title
      page.author   = params.user
      page.category = params.category
      page.minor    = params.minor
      page.summary  = params.summary
      page.content  = params.content
      sputnik:save_page(page)
      return actions.show(page, params, sputnik)
   end
end

actions.show_inner = function(page, params, sputnik)
   return sputnik:wikify_text(page.content, page.templates)
end

actions.show = function(page, params, sputnik)
   page.inner_html = actions.show_inner(page, params, sputnik)
   return page.wrappers.default(page, params, sputnik)
end

actions.logout = function(page, params, sputnik)
   params.user=nil
   page.inner_html = cosmo.fill(page.templates.LOGGED_OUT,		     
				{  url   = page.urls:show(),
				   title = page.name } )
   return page.wrappers.default(page, params, sputnik)
end

actions.complete_history = function(page, params, sputnik)
   params.show_complete_history = 1
   return actions.history(page, params, sputnik)  
end


local MONTHS = { 
   {"Jan", 31},
   {"Feb", 28},
   {"Mar", 31},
   {"Apr", 30},
   {"May", 31},
   {"Jun", 30},
   {"Jul", 31},
   {"Aug", 31},
   {"Sep", 30},
   {"Oct", 31},
   {"Nov", 30},
   {"Dec", 31} }

actions.history = function(page, params, sputnik)

   local page_name = page.name
   local datelink = function(month, date, template) 
      local params = { date = string.format(template, month, date) }
      return page.urls:history(params)
   end

   if params.show_complete_history then
      page_name = nil
      datelink = function(month, date, template) 
         return sputnik:pseudo_page("Special:History").urls:show{
	    date = string.format(template, month, date) 
	 }
      end
   end
   local title_filter = function(x) return x end

   local limit = 50
   if params.date and params.date:len() == 10 then 
      limit = 200
   end

   local pages = sputnik:get_history(page_name, limit, params.date)

   local date = params.date 
   if not date then
      local d = os.date("*t")
      date = string.format("%02d-%02d-%02d", d.year, d.month, d.day)
   end
   local cur_month = date:sub(1,7)
   local cur_mm = date:sub(6,7)
   if cur_mm:len() > 0 then cur_mm = tonumber(cur_mm) end
   local cur_year = date:sub(1,4)
   local cur_date = date:sub(9,11)
   if cur_date:len() > 0 then cur_date = tonumber(cur_date) end

   tab = { 
      do_revisions = function()
	 for i, p in ipairs(pages) do
	    cosmo.yield{
	       version_url = p.urls:show{version = p.version},
	       version     = p.version,
	       title       = title_filter(p.title),
	       author_url  = sputnik:make_url("User:" .. p.author),
	       author      = p.author,
	       if_summary  = cosmo.cond(p.summary 
					and p.summary:len() > 0,
					{summary = p.summary}),
	    }
	 end
      end, 
      current_month = cur_month,
      current_year = cur_year,
      do_dates = function() 
	 for i=1,MONTHS[cur_mm][2] do
	    cosmo.yield {
	       if_current_date = cosmo.cond( i == cur_date,
					    {  date = i}
					 ),
	       if_other_date   = cosmo.cond( not (i == cur_date),
					    {  date = i,
					       date_link = datelink(cur_month, 
								    i,
								    "%s-%02d")
					    }
					 ),
	    }
	 end
      end,
      do_months = function() 
         for i=1,12 do
	    cosmo.yield {
	       if_current_month = cosmo.cond( i == 5, 
					     {  month = MONTHS[i][1],}
					  ),
	       if_other_month   = cosmo.cond( not (i == 5),
					     {  month = MONTHS[i][1],
						month_link = datelink(
						   i, nil, "2007-%02d")
					     }
					  ),
	    }
	 end
      end
   }

   page.inner_html = cosmo.fill(page.templates.HISTORY, tab)
   return page.wrappers.default(page, params, sputnik)
end

actions.login_form = function(page, params, sputnik)
   page.inner_html = cosmo.fill(page.templates.LOGIN, 
				{  action_url = sputnik.config.BASE_URL,
				   page_name  = sputnik:dirify(page.name) } )
   return page.wrappers.default(page, params, sputnik)
end

NOT_LOGGED_IN = "not_logged_in"

actions.raw_edit = function (page, params, sputnik)
   params.raw_edit = 1
   if params.try_again then
      page.raw = params.raw_page
   end
   return actions.edit(page, params, sputnik)
end

actions.edit = function (page, params, sputnik)
   if params.try_again then
      for k, v in pairs(params) do
	 page[k] = v
      end
      page.name = params.page_name
   end

   local template = page.templates.EDIT
   local bad_fields = false
   if params.raw_edit then 
      template = page.templates.RAW_EDIT
   else
      if page.raw:len() > 0 then
	 local reloaded_page = sputnik:load_config(page.raw, {})
	 for k,v in pairs(reloaded_page) do
	    if reloaded_page[k] and sputnik.storage:is_extra_field(k) then
	       if not bad_fields then
		  bad_fields = {}
	       end
	       table.insert(bad_fields, k)
	    end
	 end
      end
   end

   page.inner_html = cosmo.fill(template,
      {
	 if_bad_fields  = cosmo.cond(bad_fields,
				     {offenders = table.concat(
					bad_fields or {}, ", ")
				     }
								 
				    ),
	 if_try_again     = cosmo.cond(params.try_again == NOT_LOGGED_IN, {}),
	 if_logged_in     = cosmo.cond(params.user,
				       {  user = params.user,
					  logout_url = page.urls:logout()
				       }),
	 if_not_logged_in = cosmo.cond(not params.user, {}),
	 action_url       = sputnik.config.BASE_URL,
	 page_name        = sputnik:dirify(page.name),
	 title            = page.title,
	 category         = page.category,
	 content          = sputnik:escape(page.content),
	 raw              = sputnik:escape(page.raw),
	 edit_link        = page.urls:edit(),
	 raw_edit_link    = page.urls:raw_edit(),
      } )
   return page.wrappers.default(page, params, sputnik)
end

actions.diff = function(page, params, sputnik)
   local other_page = sputnik:get_page(page.name, params.other) 
   page.inner_html  = cosmo.fill(page.templates.DIFF,
      {  version1 = params.version,
	 url1     = page.urls:show{version=params.version},
	 author1  = page.author,
         version2 = params.other,
	 url2     = page.urls:show{version=params.other},
	 author2  = other_page.author,
	 diff     = HtmlDiff:get_html_diff(other_page.content, page.content) 
      } )
   return page.wrappers.default(page, params, sputnik)
end

actions.raw_content = function(page, params, sputnik)
   return page.content, "text/plain"
end

actions.raw = function(page, params, sputnik)
   return page.raw or "No source available.", "text/plain"
end

actions.code = function(page, params, sputnik)
   local escaped = sputnik:escape(page.content) 
   page.inner_html = "<pre><code>" .. escaped .. "</code></pre>"
   return page.wrappers.default(page, params, sputnik)
end

wrappers = {}

wrappers.default = function(page, params, sputnik) 
   --local nav_page = sputnik.config.NAVIGATION_OVERRIDES[page.name] 
   --                 or sputnik.config.DEFAULT_NAVIGATION_BAR

   local cookie = ""
   if params.user and params.auth_token then
      cookie = params.user .. "|" .. params.auth_token
   end

   return cosmo.fill(page.templates.MAIN, {  
      title            = page.title,
      base_css_dir     = sputnik.config.BASE_CSS_DIR,
      local_css_url    = sputnik.config.LOCAL_CSS_URL,
      nav_bar          = get_nav_bar(page, params, sputnik),
      cookie_name      = sputnik.config.COOKIE_NAME,
      cookie_value     = cookie,
      if_old_version   = cosmo.cond(params.version 
				    and not (page.version 
					     == page.history.current),
				    {version = params.version} 
				 ),
      if_logged_in     = cosmo.cond( params.user,
				    {  user       = params.user,
				       logout_url = page.urls:logout() }
				 ),
      if_not_logged_in = cosmo.cond( not params.user,
				    { login_form_url = page.urls:login_form() }
				 ),
      if_edit_button   = cosmo.cond( not ( params.action == "login_form" 
					   or params.action == "logout") ,
		            {  talk_url    = sputnik:make_url(
					        sputnik:talk(page.name)),
			       page_url    = sputnik:make_url(
					        sputnik:untalk(page.name)),
			       edit_url    = page.urls:edit{
				  version = params.version 
			       },
			       history_url = page.urls:history()
			    }
			 ),
      content          = page.inner_html,
      sputnik_link     = "http://www.freewisdom.org/projects/sputnik/",
      rss_link         = sputnik:pseudo_page("Special:RSS").urls:rss(),
      if_has_history   = cosmo.cond(page.history and page.author, 
				    get_history_table(page, sputnik)),
      if_no_history    = cosmo.cond(not (page.history and page.author),{}),

   } )

end
